﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.UI;

namespace ExtenDev.Web.UI
{
    [ControlBuilder(typeof(ClientTemplateControlBuilder))]
    public class ClientTemplate : Control
    {
        private string templateId;
        private List<ClientTemplate> subTemplates;

        public ClientTemplate() {
            BlockStartIdentifier = BlockStopIdentifier = "#";
            EvaluationIdentifier = "#=";
            PreCompile = true;
            subTemplates = new List<ClientTemplate>();
        }

        //TODO: add identifier and precompile inheritance to subtemplates without properties set
        public bool PreCompile { get; set; }

        //primarily used for subtemplates, string name of client variable from scope of parent template to use as data for subtemplate evaluation
        public string ClientDataArgument { get; set; }

        public string BlockStartIdentifier { get; set; }
        public string BlockStopIdentifier { get; set; }
        public string EvaluationIdentifier { get; set; }

        public string TemplateId {
            get {
                return templateId ?? this.ClientID;
            }
            set {
                templateId = value;
            }
        }

        protected bool IsSubTemplate { get; set; }
        protected ClientTemplate ParentTemplate { get; set; }

        
        protected override void AddParsedSubObject(object obj)
        {
            if (obj is ClientTemplate)
            {
                ClientTemplate sub = (ClientTemplate)obj;
                sub.IsSubTemplate = true;
                sub.ParentTemplate = this;

                subTemplates.Add(sub);
            }
            base.AddParsedSubObject(obj);
        }

        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            if (!this.IsSubTemplate)
            {
                foreach (ClientTemplate sub in subTemplates)
                {
                    sub.RenderScriptBlock(writer);
                }
                RenderScriptBlock(writer);
            }
            else
            {
                string sub = "<{0} $.getTemplateFunction('{1}').call({2},{2}) {3}>";
                string cda = ClientDataArgument;
                string tmplId = TemplateId.Replace("'","\\'");

                if (string.IsNullOrWhiteSpace(cda)) cda = "this";

                writer.Write(sub,
                    ParentTemplate.EvaluationIdentifier, tmplId, cda, ParentTemplate.BlockStopIdentifier);

                //TODO: add noConflict style support within templates
            }

            
        }

        private void RenderScriptBlock(System.Web.UI.HtmlTextWriter writer)
        {
            string tmplId = this.TemplateId;

            //todo: add option to replace the following with a resource manager call.
            string itemId = tmplId + "_MicroTemplateRegistered";
            if (!Context.Items.Contains(itemId))
            { //only includes the client template once.
                Context.Items.Add(itemId, itemId);

                if (PreCompile)
                {
                    StringWriter sw = new StringWriter();
                    HtmlTextWriter tw = new HtmlTextWriter(sw);

                    base.Render(tw);

                    string js = @"<script type=""text/javascript"">
(function($) {{
    var func = function(data) {{
        {0}
    }};

    if ($ && $.isFunction($.getTemplateFunction)) {{
        $.getTemplateFunction({{
            templateId: '{1}',
            templateFunction: func
        }});
    }} else {{
        (window.templateCache = window.templateCache || {{}})['{1}'] = func;
    }}
}})(window.jQuery);
</script>";

                    string func = MicroTemplateCompiler.GenerateTemplateScript(
                        sw.ToString(), BlockStartIdentifier, BlockStopIdentifier, EvaluationIdentifier);

                    writer.WriteLine(js, func, tmplId.Replace("'","\\'"));
                }
                else
                {
                    writer.WriteLine("<script id=\"{0}\" type=\"text/html\">", HttpUtility.HtmlAttributeEncode(tmplId));
                    base.Render(writer);
                    writer.WriteLine();
                    writer.Write("</script>");
                }
            }
        }
    }

    public class ClientTemplateControlBuilder : ControlBuilder
    {
        public override Type GetChildControlType(string tagName, System.Collections.IDictionary attribs)
        {
            int schIdx = tagName.IndexOf(":");
            if (schIdx > 0 && schIdx < tagName.Length - 1)
            {
                tagName = tagName.Substring(schIdx + 1);
            }

            if ("SubTemplate".Equals(tagName, StringComparison.InvariantCultureIgnoreCase))
            {
                return typeof(SubTemplate);
            }
            return base.GetChildControlType(tagName, attribs);
        }
    }

    public class SubTemplate : ClientTemplate
    {
        protected override void Render(HtmlTextWriter writer)
        {
            if (ParentTemplate == null)
                throw new InvalidOperationException("SubTemplates must be within a ClientTemplate"); 
            base.Render(writer);
        }
        
    }
}
